package com.sjp.myspring.web.servlet;

import com.sjp.myspring.annotation.JPController;
import com.sjp.myspring.annotation.JPRequestMapping;
import com.sjp.myspring.context.JPApplicationContext;
import com.sjp.myspring.context.support.JPWebApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;

//@Slf4j
public class DispatcherServlet extends HttpServlet {

    private static final String CONFIG_LOCATIONS = "configLocations";

    private JPApplicationContext applicationContext;
    private List<JPHandlerMapping> handlerMappings = new ArrayList<>();
    private List<JPHandlerAdapter> handlerAdapters = new ArrayList<>();
    private List<JPViewResolver> viewResolvers = new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        String configLocations = config.getInitParameter(config.getInitParameter(CONFIG_LOCATIONS));

        applicationContext = new JPWebApplicationContext(configLocations);
        applicationContext.refresh();

        initStrategies(applicationContext);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            JPModelAndView mv = new JPModelAndView("500");
            try {
                processDispatchResult(req, resp, mv);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        JPHandlerMapping handlerMapping = getHandlerMapping(req);
        if (handlerMapping == null) {
            //404
            JPModelAndView mv = new JPModelAndView("404");
            processDispatchResult(req, resp, mv);
            return;
        }
        //准备调用的参数
        JPHandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);
        if (handlerAdapter == null) {
            processDispatchResult(req, resp, new JPModelAndView("500"));
            return;
        }
        //调用方法
        //modelAndView 中存储了要返回到页面的值和模版名
        JPModelAndView mv = handlerAdapter.handle(req, resp, handlerMapping);

        processDispatchResult(req, resp, mv);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, JPModelAndView mv) throws Exception {
        if (mv == null) {
            return;
        }
        if (viewResolvers.isEmpty()) {
            return;
        }
        for (JPViewResolver viewResolver : viewResolvers) {
            JPView view = viewResolver.resolveViewName(mv.getViewName(), null);
            view.render(mv.getModel(), req, resp);
        }

    }

    private JPHandlerAdapter getHandlerAdapter(JPHandlerMapping handlerMapping) {
        if (handlerAdapters == null) {
            return null;
        }
        for (JPHandlerAdapter handlerAdapter : handlerAdapters) {
            if (handlerAdapter.supports(handlerMapping)) {
                return handlerAdapter;
            }
        }
        return null;
    }

    private JPHandlerMapping getHandlerMapping(HttpServletRequest req) {
        if (this.handlerMappings.isEmpty()) {
            return null;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        for (JPHandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if (matcher.matches()) {
                return handlerMapping;
            }
        }
        return null;
    }

    protected void initStrategies(JPApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);

        //必须实现
        initHandlerMappings(context);
        //必须实现
        initHandlerAdapters(context);

        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);

        //初始化视图转换器  必须实现
        initViewResolvers(context);

        initFlashMapManager(context);
    }

    private void initViewResolvers(JPApplicationContext context) {
        //拿到一个模版的存放目录
        Properties config = context.getConfig();
        String templateRoot = config.getProperty("templateRoot");
        String path = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        this.viewResolvers.add(new JPViewResolver(templateRoot));

        //File templateRootDir = new File(path);
        //for (File file : templateRootDir.listFiles()) {
        //
        //}

    }

    private void initRequestToViewNameTranslator(JPApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(JPApplicationContext context) {
    }

    /**
     * 把request请求变成一个handler，参数都是字符串的，要自动匹配到handler中的形参
     *
     * @param context
     */
    private void initHandlerAdapters(JPApplicationContext context) {
        handlerAdapters.add(new JPHandlerAdapter());
    }

    private void initHandlerMappings(JPApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames) {
                Object bean = context.getBean(beanName);
                Class<?> beanClass = bean.getClass();
                if (!beanClass.isAnnotationPresent(JPController.class) || !beanClass.isAnnotationPresent(JPRequestMapping.class)) {
                    continue;
                }
                JPRequestMapping annotation = beanClass.getAnnotation(JPRequestMapping.class);
                String baseUrl = "/" + annotation.value()[0];
                Method[] methods = beanClass.getDeclaredMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(JPRequestMapping.class)) {
                        continue;
                    }
                    JPRequestMapping requestMapping = method.getAnnotation(JPRequestMapping.class);

                    String url = (baseUrl + "/" + requestMapping.value()[0].replaceAll("\\*", ".*"))
                            .replaceAll("/+", "");

                    this.handlerMappings.add(new JPHandlerMapping(url, bean, method));
                    //log.info("handlerMapping url="+url, bean);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void initThemeResolver(JPApplicationContext context) {
    }

    private void initFlashMapManager(JPApplicationContext context) {

    }

    private void initLocaleResolver(JPApplicationContext context) {
    }

    private void initMultipartResolver(JPApplicationContext context) {
    }


}
